/**
 *
 * @author: Marek Zakowicz <mazak (at) debian (dot) linux.org.pl>
 *
 * Copyright (c) 2005, 2006, 2007 Marek Zakowicz. All rights reserved.
 *
 * Licence: This code may be used, (re)distribute and/or modify under the terms one of the following two kinds of licence:
 *          1) as free software under GPL 3 (General Public Licence ver. 3 as published by the Free Software Foundation)
 *          2) as software under the terms individual licence from Marek Zakowicz
 *
 *          This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *          without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

package jsp2php;

import java.util.*;
import java.lang.reflect.*;

// Base class for translators of object types
abstract class TranslatorBase extends PrimitiveBaseTranslator {

   protected LinkedHashMap<String, Translation> fields = new LinkedHashMap<String, Translation>();
   private HashMap<String, Method> methods = new HashMap<String, Method>();

   TranslatorBase() {
      //System.err.println("Class " + this.getClass());
      for( Method m : this.getClass().getMethods() ) {
         String name = m.getName();
         if(name.startsWith("translate_")) {
            methods.put(name.substring(10), m);
            //System.err.println("add " + name.substring(10));
         }
         else {
            //System.err.println("ignore " + name);
         }
      }
   }

   public Translation call(String id, Translation ... args) throws TranslatorException {
      //System.err.println("call "+ id);
      Translation tn = new Translation();
      Method m = methods.get(id);
      if(m == null) {
         throw new TranslatorException(this.getClass() + " doesnt support method "+ id +"!");
      }
      try {
         //Method m = StringTranslator.class.getMethod("translate_"+id, Translation[].class);
         return (Translation) m.invoke(this, (java.lang.Object) args);
      }
      catch(Exception e) {
         throw new TranslatorException("Error during "+ name() +"."+ id +" invocation for "+ TranslatorUtil.call_description(args) +" [Exception: " + e +" Cause: "+ e.getCause() +"]");
      }
   }

   public Translation field(String id) throws TranslatorException {
      Translation tn=fields.get(id);
      if(tn==null) {
         throw new TranslatorException("Unknown field "+ name() +"."+ id);
      }
      return tn;
   }


   public String php_name() throws TranslatorException {
      throw new TranslatorException("Objects of "+ name() +" are not translated to PHP!");
   }

   public Translation cast(JavaType target_type, Translation arg) throws TranslatorException {
      if(target_type != arg.type) {
         if(arg.consistent) {
            return new Translation("("+ target_type.translator().php_name() +") " + arg.text, target_type, false, arg.not_null);
         }
         else {
            return new Translation("("+ target_type.translator().php_name() +") (" + arg.text + ")", target_type, false, arg.not_null);
         }
      }
      else {
         return arg;
      }
   }

   // For dummy ValueTranslator
   public Translation literal(String value) throws TranslatorException {
      throw new TranslatorException(name() + " class doesnt have literals!");
   }


   Translation translate(String java_method, String php_format, int expected_args_no, JavaType result_type, JavaType expected_types[], boolean not_null, Translation[] args) throws TranslatorException {
      return translate(java_method, php_format, expected_args_no, result_type, expected_types, not_null, args, true);
   }

   Translation translate(String java_method, String php_format, int expected_args_no, JavaType result_type, JavaType expected_types[], boolean not_null, Translation[] args, boolean consistent) throws TranslatorException {
      // metoda z wieloma argumentami
      validate_arg_no(expected_args_no, java_method, args);
      validate_arg_type(expected_types, java_method, args);

      String text_args[] = new String[args.length];
      int i=0;
      for(Translation a : args) {
         text_args[i] = args[i].text;
         i++;
      }

      return new Translation(String.format(php_format, (Object[]) text_args), result_type, consistent, not_null);
   }

   Translation translate(String java_method, String php_format, JavaType result_type, JavaType expected_type, boolean not_null, Translation[] args, boolean consistent) throws TranslatorException {
      // metoda z jednym argumentem
      return translate( java_method, php_format, 1, result_type, new JavaType[] {expected_type}, not_null, args, consistent);
   }

   Translation translate(String java_method, String php_format, JavaType result_type, boolean not_null, Translation ... args) throws TranslatorException {
      return translate(java_method, php_format, 0, result_type, null, not_null, args);
   }

   Translation static_translate(String java_method, String php_format, JavaType expected_type, JavaType result_type, Translation ... args) throws TranslatorException {
      // metoda statyczna z 1 argumentem
      validate_arg_no(1, java_method, args);
      validate_arg_type(expected_type, java_method, args);

      Translation arg = args[1];
      return new Translation(String.format(php_format, arg.text), result_type, true, arg.not_null);
   }

   void validate_arg_no(int expected_no, String method, Translation ... args) throws TranslatorException {
      if( args.length != expected_no + 1 ) {
         throw new TranslatorException("Method "+ name() +"."+ method +" support "+ expected_no +" args, but needed "+ (args.length-1) +" !");
      }
   }

   void validate_arg_no(int expected_nos[], String method, Translation ... args) throws TranslatorException {
      int len = args.length - 1;
      for(int no : expected_nos) {
         if(no == len) {
            return;
         }
      }
      throw new TranslatorException("Method "+ name() +"."+ method +" doesn't support "+ len +" args!");
   }

   void validate_arg_type(JavaType expected_type, String method, Translation ... args) throws TranslatorException {
      Translation arg = args[1];
      if( expected_type != arg.type ) {
         throw new TranslatorException("'"+ expected_type.name() +"' type expected instead of '"+ arg.type.name() +"' in method "+ name() +"."+ method +"!");
      }
   }

   void validate_arg_type(JavaType expected_types[], String method, Translation ... args) throws TranslatorException {
      int len = args.length;
      int i = -2;
      for(Translation arg : args) {
         i++;
         if(i == -1) {
            continue;
         }
         if(i>=len) {
            break;
         }
         JavaType type = expected_types[i];
         if(arg.type != type) {
            throw new TranslatorException("'"+ type.name() +"' type expected instead of '"+ arg.type.name() +"' in "+ (i+1) +" arg of method "+ name() +"."+ method +"!");
         }
      }
   }

}
